package com.jdk.concurrent;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Pattern;

/**
 * 
 * @author jackson.zhang
 *
 */
public class PackageUtils {

	public final static List<String> EMPTY_LIST = new ArrayList<String>(0);

	/**
	 * 查找指定package下的class
	 *
	 * @param packageName
	 *            package名字 ，允许正则表达式
	 * @return符合要求的classes
	 */
//	public static Set<Class<?>> findClassesInPackage(String packageName) {
//		return findClassesInPackage(packageName, EMPTY_LIST, EMPTY_LIST);
//	}

	public static Set<String> findClassesInPackage(String packageName) {
		return findClassesInPackage(packageName, EMPTY_LIST, EMPTY_LIST);
	}

	/**
	 * 查找指定package下的class
	 *
	 * @param packageName
	 *            package名字，允许正则表达式
	 * @param included
	 *            包含的类名(短类名，不包含package前缀)，允许正则表达式
	 * @param excluded
	 *            不包含的类名(短类名，不包含package前缀)，允许正则表达式
	 * @return 符合要求的classes，全路径名
	 * @throws java.io.IOException
	 */
	public static Set<String> findClassesInPackage(String packageName,
			List<String> included, List<String> excluded) {
		long begin = System.currentTimeMillis();
		// 第一个class类的集合
		Set<String> classes = new LinkedHashSet<String>();
		String packageOnly = packageName;
		// 递归的判断包下所有类
		boolean recursive = true;
		// if (packageName.endsWith(".*")) {
		// packageOnly = packageName.substring(0,
		// packageName.lastIndexOf(".*"));
		// recursive = true;
		// }
		String packageDirName = packageOnly.replace('.', '/');
		// 定义一个枚举的集合 并进行循环来处理这个目录下的things
		try {
			Enumeration<URL> dirs = Thread.currentThread()
					.getContextClassLoader().getResources(packageDirName);
			while (dirs.hasMoreElements()) {
				URL url = (URL) dirs.nextElement();
				String protocol = url.getProtocol();
				// 如果是以文件的目录结构
				if ("file".equals(protocol)) {
					// 获取包的物理路径
					String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
					// 以文件的方式扫描整个包下的文件 并添加到集合中
					findClassesInDirPackage(packageName, filePath, recursive,
							included, excluded, classes);
					// 如果是jar结构
				} else if ("jar".equals(protocol)) {
					JarFile jar = ((JarURLConnection) url.openConnection())
							.getJarFile();
					Enumeration<JarEntry> entries = jar.entries();
					while (entries.hasMoreElements()) {
						// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
						JarEntry entry = entries.nextElement();
						String name = entry.getName();
						// 如果是以/开头的
						if (name.charAt(0) == '/') {
							// 获取后面的字符串
							name = name.substring(1);
						}
						// 如果前半部分和定义的包名相同
						if (name.startsWith(packageDirName)) {
							int idx = name.lastIndexOf('/');
							// 如果以"/"结尾 是一个包
							if (idx != -1) {
								// 获取包名 把"/"替换成"."
								packageName = name.substring(0, idx).replace(
										'/', '.');
							}
							// 如果可以迭代下去 并且是一个包
							if ((idx != -1) || recursive) {
								// 如果是一个.class文件 而且不是目录
								if (name.endsWith(".class")
										&& !entry.isDirectory()) {
									// 去掉后面的".class" 获取真正的类名
									String className = name.substring(
											packageName.length() + 1,
											name.length() - 6);
									includeOrExcludeClass(packageName,
											className, included, excluded,
											classes);
								}
							}
						}
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		long end = System.currentTimeMillis();
		System.out.println(end - begin);
		System.out.println("classsize :" + classes.size());
		return classes;
	}
//	public static Set<Class<?>> findClassesInPackage(String packageName,
//			List<String> included, List<String> excluded) {
//		long begin = System.currentTimeMillis();
//		// 第一个class类的集合
//		Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
//		String packageOnly = packageName;
//		// 递归的判断包下所有类
//		boolean recursive = true;
//		// if (packageName.endsWith(".*")) {
//		// packageOnly = packageName.substring(0,
//		// packageName.lastIndexOf(".*"));
//		// recursive = true;
//		// }
//		String packageDirName = packageOnly.replace('.', '/');
//		// 定义一个枚举的集合 并进行循环来处理这个目录下的things
//		try {
//			Enumeration<URL> dirs = Thread.currentThread()
//					.getContextClassLoader().getResources(packageDirName);
//			while (dirs.hasMoreElements()) {
//				URL url = (URL) dirs.nextElement();
//				String protocol = url.getProtocol();
//				// 如果是以文件的目录结构
//				if ("file".equals(protocol)) {
//					// 获取包的物理路径
//					String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
//					// 以文件的方式扫描整个包下的文件 并添加到集合中
//					findClassesInDirPackage(packageName, filePath, recursive,
//							included, excluded, classes);
//					// 如果是jar结构
//				} else if ("jar".equals(protocol)) {
//					JarFile jar = ((JarURLConnection) url.openConnection())
//							.getJarFile();
//					Enumeration<JarEntry> entries = jar.entries();
//					while (entries.hasMoreElements()) {
//						// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
//						JarEntry entry = entries.nextElement();
//						String name = entry.getName();
//						// 如果是以/开头的
//						if (name.charAt(0) == '/') {
//							// 获取后面的字符串
//							name = name.substring(1);
//						}
//						// 如果前半部分和定义的包名相同
//						if (name.startsWith(packageDirName)) {
//							int idx = name.lastIndexOf('/');
//							// 如果以"/"结尾 是一个包
//							if (idx != -1) {
//								// 获取包名 把"/"替换成"."
//								packageName = name.substring(0, idx).replace(
//										'/', '.');
//							}
//							// 如果可以迭代下去 并且是一个包
//							if ((idx != -1) || recursive) {
//								// 如果是一个.class文件 而且不是目录
//								if (name.endsWith(".class")
//										&& !entry.isDirectory()) {
//									// 去掉后面的".class" 获取真正的类名
//									String className = name.substring(
//											packageName.length() + 1,
//											name.length() - 6);
//									includeOrExcludeClass(packageName,
//											className, included, excluded,
//											classes);
//								}
//							}
//						}
//					}
//				}
//			}
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//		long end = System.currentTimeMillis();
//		System.out.println(end - begin);
//		System.out.println(classes.size());
//		return classes;
//	}

	private static void findClassesInDirPackage(String packageName,
			String filePath, final boolean recursive, List<String> included,
			List<String> excluded, Set<String> classes) {
		File dir = new File(filePath);
		// 如果不存在或者 也不是目录就直接返回
		if (!dir.exists() || !dir.isDirectory()) {
			return;
		}
		// 如果存在 就获取包下的所有文件 包括目录
		File[] dirfiles = dir.listFiles(new FileFilter() {
			// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
			@Override
			public boolean accept(File file) {
				return (recursive && file.isDirectory())
						|| (file.getName().endsWith(".class"));
			}
		});

		for (File file : dirfiles) {
			// 递归处理
			if (file.isDirectory()) {
				findClassesInDirPackage(packageName + "." + file.getName(),
						file.getAbsolutePath(), recursive, included, excluded,
						classes);
			} else {
				// 去掉后面的".class" 获取真正的类名
				String className = file.getName().substring(0,
						file.getName().length() - 6);
				includeOrExcludeClass(packageName, className, included,
						excluded, classes);
			}
		}
	}

	/**
	 * include exclude过滤
	 *
	 * @param packageName
	 *            包名，确定名
	 * @param className
	 *            短类名，不包含package前缀，确定名
	 * @param included
	 *            包含的类名(短类名，不包含package前缀)，允许正则表达式
	 * @param excluded
	 *            不包含的类名(短类名，不包含package前缀)，允许正则表达式
	 * @param classes
	 *            结果集
	 */
	private static void includeOrExcludeClass(String packageName,
			String className, List<String> included, List<String> excluded,
			Set<String> classes) {
		if (isIncluded(className, included, excluded)) {
				System.out.println(packageName + '.' + className);
				classes.add(packageName + '.' + className);
		}
	}

	/**
	 * 是否包含
	 *
	 * @param name
	 *            短类名，不包含package前缀，确定名
	 * @param included
	 *            包含的类名(短类名，不包含package前缀)，允许正则表达式
	 * @param excluded
	 *            不包含的类名(短类名，不包含package前缀)，允许正则表达式
	 * @return include-true,else-false
	 */
	private static boolean isIncluded(String name, List<String> included,
			List<String> excluded) {
		boolean result = false;
		if (included.size() == 0 && excluded.size() == 0) {
			result = true;
		} else {
			boolean isIncluded = find(name, included);
			boolean isExcluded = find(name, excluded);
			if (isIncluded && !isExcluded) {
				result = true;
			} else if (isExcluded) {
				result = false;
			} else {
				result = included.size() == 0;
			}
		}
		return result;
	}

	private static boolean find(String name, List<String> list) {
		for (String regexpStr : list) {
			if (Pattern.matches(regexpStr, name)) {
				return true;
			}
		}
		return false;
	}

	public static void main(String[] args) {
		findClassesInPackage("com.road.yishi");
	}

}
